package com.hucsmn.studentms;

import java.util.*;

/**
 * 学生信息数据库。
 */
public class Database implements Iterable<Student> {
    // 使用有序列表储存学生信息。
    private final List<Student> db = new ArrayList<>();

    /**
     * 建立空数据库。
     */
    Database() {
    }

    /**
     * 导入数据库，当学生信息存在重复学号时，导入失败抛出异常。
     *
     * @param source 数据源。
     * @throws IllegalArgumentException 学生信息存在重复学号时，抛出异常。
     */
    Database(List<Student> source) throws IllegalArgumentException {
        for (Student s : source) {
            if (!add(s)) {
                throw new IllegalArgumentException("学号重复");
            }
        }
    }

    /**
     * 查找指定学号的学生是否已经录入。
     *
     * @param number 要查找的学号。
     * @return 查找到已录入则返回真，否则返回假。
     */
    public boolean contains(int number) {
        int index = Collections.binarySearch(db, Student.getSearchKey(number));
        return index >= 0;
    }

    /**
     * 查找指定学号的学生信息，不存在则返回 null。
     *
     * @param number 要查找的学号。
     * @return 查找到的学生信息，不存在时返回 null。
     */
    public Student get(int number) {
        int index = Collections.binarySearch(db, Student.getSearchKey(number));
        if (index < 0) {
            return null;
        }
        return db.get(index);
    }

    /**
     * 向数据库中添加学生信息，如果学号重复则录入失败。
     *
     * @param student 学生信息。
     * @return 学生信息录入成功时返回真，否则返回假。
     */
    public boolean add(Student student) {
        int index = Collections.binarySearch(db, student);
        if (index >= 0) {
            return false;
        }
        index = -(index + 1);
        db.add(index, student);
        return true;
    }

    /**
     * 删除指定学号的学生信息。
     *
     * @param number 要删除的学号。
     * @return 学生信息删除成功时返回真，否则返回假。
     */
    public boolean remove(int number) {
        int index = Collections.binarySearch(db, Student.getSearchKey(number));
        if (index < 0) {
            return false;
        }
        db.remove(index);
        return true;
    }

    /**
     * 更改指定学号的学生信息，本操作可以更改学号。
     * 当要查找的学号不存在，或者新的学号已经被占用时，更新操作不会进行并返回 null。
     *
     * @param number  要更新的学号。
     * @param student 要更新的学生信息。
     * @return 如果成功更新学生信息，则返回旧的值，否则返回 null。
     */
    public Student update(int number, Student student) {
        int delPos = Collections.binarySearch(db, Student.getSearchKey(number));
        if (delPos < 0) {
            return null;
        }

        // 不更改学号时，直接原位更改。
        if (student.getNumber() == number) {
            Student old = db.get(delPos);
            db.set(delPos, student);
            return old;
        }

        int insPos = Collections.binarySearch(db, Student.getSearchKey(student.getNumber()));
        if (insPos >= 0) {
            return null;
        }
        insPos = -(insPos + 1);

        // 平移新位置和旧位置之间的元素，避免扩容。
        Student old;
        if (insPos >= delPos) {
            Collections.rotate(db.subList(delPos, insPos), -1);
            old = db.get(insPos - 1);
            db.set(insPos - 1, student);
        } else {
            Collections.rotate(db.subList(insPos, delPos + 1), 1);
            old = db.get(insPos);
            db.set(insPos, student);
        }
        return old;
    }

    /**
     * 获取学生信息数据的迭代器。
     *
     * @return 学生信息的迭代器。
     */
    @Override
    public Iterator<Student> iterator() {
        return db.iterator();
    }
}
